from threading import Thread
import time
from main.process import ProcessStates, ProcessStatus
from main.processHandling import updatePCB
from time import sleep

class CPU(Thread):
    def __init__(self,readyProcessesQueue,anIO,aMemory,aDisk):
        Thread.__init__(self)
        self.readyProcessesQueue = readyProcessesQueue
        self.currentProcess = None
        self.io = anIO
        self.kernel = None
        self.memory = aMemory
        self.disk = aDisk
        
    def run(self):
        while(True):
            print('CPU NOT EXECUTING')
            if(not self.isNotWorking()):
                self.executeProcess()
                print('CPU EXECUTED A PROCESS')
            sleep(1)    
        
        
    def addKernelToCpu(self,aKernel):
        self.kernel = aKernel
        
    def isNotWorking(self):
        return (self.currentProcess == None)  
            
    def executeProcess(self):
        if(not self.memory.hasThisProcess(self.currentProcess)):#Si no lo tiene en memoria se fija si lo puede buscar
            candidate = self.disk.downloadFromDisk(self.currentProcess)
            if(self.memory.hasSpace(candidate.sizeOfAProcess())):
                self.memory.addingPolicy.saveProcessInMemory(candidate,self.memory)#Lo agrega en memoria
                self.memory.processesTable.append(candidate) 
            elif(self.memory.canSwap(candidate.sizeOfAProcess(), self)):
                self.memory.swap(candidate,self.disk,self)
        elif(self.currentProcess.pcb.currentInstruction.status == ProcessStatus.IO):
            self.currentProcess.pcb.status = ProcessStates.WAITING                                                      #Pone al proceso en estado de espera
            self.currentProcess.pcb.currentInstruction = self.memory.mmu.searchProgram(self.currentProcess,self.memory) #Busca la instruccion en memoria
            self.io.addIOProcessToQueue(self.currentProcess)
        else:
            self.executingAnInstruction()
            # Mark the process as Ready, and pop it into the RPQ
        self.currentProcess = None
        
    #Simula la ejecucion de una instruccion obtenida de memoria, atravez del calculo con MMU
    def executingAnInstruction(self):
        anInstruction = self.memory.mmu.searchProgram(self.currentProcess,self.memory)
        print("estoy ejecutando el proceso "+ str(self.currentProcess.pcb.processID))
        print("...una instruccion de CPU de: "+ str(anInstruction.timeRemaining)+" segs")#self.currentProcess.instructions[0]
        time.sleep(anInstruction.timeRemaining)
        updatePCB(self.currentProcess)
        self.sendProcessToTheCorrectQueue(self.currentProcess)
        
        
    #Envia un proceso a su cola correspondiente(terminados o listos)
    def sendProcessToTheCorrectQueue(self,aProcess):     
        if(aProcess.pcb.status == ProcessStates.FINISHED):
            self.kernel.interrupt_aProcessHasEnded(aProcess)
        else:
            self.readyProcessesQueue.append(aProcess)
            
    #Saca a un proceso de ejecucion   
    def expropiate(self):
        if(not self.currentProcess == None):
            self.readyProcessesQueue.append(self.currentProcess)
            self.currentProcess = None
            
    def executeProcess2(self,aProcess,anExecutingTime):
        t = aProcess.pcb.currentInstruction.timeRemaining - anExecutingTime
        aProcess.pcb.currentInstruction.timeRemaining = t
    